/*
 * @(#)UsfFilter.java $version 2010. 8. 11
 *
 * Copyright 2007-2010 NHN Corp. All rights Reserved. 
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.nhncorp.usf.core.servlet;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.script.ScriptException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.nhncorp.lucy.common.config.model.ApplicationInfo;
import com.nhncorp.lucy.common.util.PageEncodingHelper;
import com.nhncorp.lucy.web.encoding.AjaxPageEncoder;
import com.nhncorp.usf.core.UsfConfiguration;
import com.nhncorp.usf.core.UsfDispatcher;
import com.nhncorp.usf.core.jmx.JVMMBean;
import com.nhncorp.usf.core.jmx.WebModuleMBean;
import com.nhncorp.usf.core.result.helper.FreemarkerConfigFactory;
import com.nhncorp.usf.core.result.helper.UsfConfigurationHolder;
import com.nhncorp.usf.core.util.JMXUtil;
import com.nhncorp.usf.core.util.StringUtil;
import com.opensymphony.xwork.ActionContext;

import static com.nhncorp.lucy.web.helper.ServletHelper.*;

/**
 * USF 요청 처리.
 *
 * @author Web Platform Development Team.
 */
public class UsfFilter implements Filter {
	Log log = LogFactory.getLog(UsfFilter.class);

	private AjaxPageEncoder ajaxPageEncoder = new AjaxPageEncoder();

	private static final String ATTRIBUTE_TEMPLATES = "templates";

	private static final String ATTRIBUTE_SUFFIX = "suffix";

	private static final String DEFAULT_VALUE_TEMPLATES = "/WEB-INF/templates";

	private static final String DEFAULT_VALUE_SUFFIX = "nhn";

	private static final String ATTRIBUTE_ENABLE_SCRIPT = "enable-scripting";

	private static final String ATTRIBUTE_SCRIPTDIR = "custom-script-dir";

	private static final String ATTRIBUTE_SCRIPT_PROPERTY_FILE = "script-config-file";

	private static final String DEFAULT_VALUE_SCRIPTDIR = "/WEB-INF/scripts";

	@SuppressWarnings("unused")
	private static final String ATTRIBUTE_MONITORING = "enable-monitoring";

	/**
	 * USF 요청 처리를 위한 Dispathcer.
	 */
	private UsfDispatcher usfDispatcher;

	private JVMMBean jvmMBean = null;
	private WebModuleMBean webModuleMBean = null;
	@SuppressWarnings("unused")
	private String contextName;

	/**
	 * {@link UsfDispatcher} 생성.
	 * 
	 * @param filterConfig FilterConfig
	 * @throws ServletException the ServletException
	 */
	public void init(FilterConfig filterConfig) throws ServletException {
		log.debug("UsfFilter Initialize...");

		// set the path of templates
		String templatesPath = filterConfig.getInitParameter(ATTRIBUTE_TEMPLATES);
		
		if (StringUtil.isEmpty(templatesPath, true)) {
			templatesPath = DEFAULT_VALUE_TEMPLATES;
		}

		templatesPath = FilenameUtils.separatorsToSystem(templatesPath);
		
		if (templatesPath != null && templatesPath.indexOf(File.separator) != 0) {
			templatesPath = File.separator + templatesPath;
		}
		if (templatesPath != null && templatesPath.endsWith(File.separator)) {
			templatesPath = templatesPath.substring(0, templatesPath.length() - 1);
		}

		if (templatesPath == null) {
			throw new IllegalArgumentException("template path is null");
		}

		// set the suffix
		String suffix = filterConfig.getInitParameter(ATTRIBUTE_SUFFIX);
		
		if (StringUtil.isEmpty(suffix, true)) {
			suffix = DEFAULT_VALUE_SUFFIX;
		}

		try {
			String scriptPath = getScriptPath(filterConfig);
			usfDispatcher = UsfDispatcher.getInstance();
			UsfConfiguration usfConfiguration = new UsfConfiguration(templatesPath, suffix, scriptPath);
			String scriptPropertyFile = filterConfig.getInitParameter(ATTRIBUTE_SCRIPT_PROPERTY_FILE);
			
			if (scriptPropertyFile != null) {
				usfConfiguration.setScriptProperty(scriptPropertyFile);
			}
			
			UsfConfigurationHolder.setUsfConfiguration(usfConfiguration);
			usfDispatcher.initialize(usfConfiguration);
			
			// use default template loader
			FreemarkerConfigFactory.setTemplateLoader(null);
		} catch (Exception e) {
			throw new ServletException(e);
		}
	}

	/**
	 * Gets the script path.
	 * 
	 * @param filterConfig the filter config
	 * 
	 * @return the script path
	 * 
	 * @throws ScriptException the script exception
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	private String getScriptPath(FilterConfig filterConfig) throws ScriptException, IOException {
		String enableScripting = filterConfig.getInitParameter(ATTRIBUTE_ENABLE_SCRIPT);
		
		if (!StringUtil.isEmpty(enableScripting, true) && Boolean.parseBoolean(enableScripting)) {
			String customScriptDir = filterConfig.getInitParameter(ATTRIBUTE_SCRIPTDIR);
			
			if (StringUtil.isEmpty(customScriptDir, true)) {
				customScriptDir = DEFAULT_VALUE_SCRIPTDIR;
			}
			
			customScriptDir = FilenameUtils.separatorsToSystem(customScriptDir);
			
			if (customScriptDir != null && customScriptDir.indexOf(File.separator) != 0) {
				customScriptDir = File.separator + customScriptDir;
			}
			if (customScriptDir != null && customScriptDir.endsWith(File.separator)) {
				customScriptDir = customScriptDir.substring(0, customScriptDir.length() - 1);
			}
			
			return customScriptDir;
		}
		
		return null;
	}

	/**
	 * check whether actionUri is usf or not. If it is not action of usf, next filter is actived.
	 * 
	 * @param request ServletRequest
	 * @param response ServletResponse
	 * @param chain FilterChain
	 * @throws IOException the IOException
	 * @throws ServletException the ServletException
	 */
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest servletRequest = (HttpServletRequest)request;
		HttpServletResponse servletResponse = (HttpServletResponse)response;

		// extract actionUri from request and check actionUri
		if (usfDispatcher.isValidAction(servletRequest)) {
			String actionUri = usfDispatcher.getActionUri(servletRequest);
			
			// set the encoding of request
			if (!isMultiPart(servletRequest)) {
				Map<String, Object> map = new HashMap<String, Object>();
				map.put(HTTP_REQUEST, request);

				String pageEncoding = PageEncodingHelper.getEncoding(map, null);
				
				if (pageEncoding == null) {
					// for low-version compatibility.
					pageEncoding = (ajaxPageEncoder.isAllowedEncoder(map)) ? ajaxPageEncoder.getEncode()
						: ApplicationInfo.getPageEncoding();
				}
				
				setCharacterEncoding(servletRequest, pageEncoding);
			}

			// execute action using usfDispatcher
			ActionContext actionContext = ActionContext.getContext();
			actionContext.put(HTTP_REQUEST, servletRequest);
			actionContext.put(HTTP_RESPONSE, servletResponse);
			actionContext.put(SERVLET_CONTEXT, ServletContextHolder.get());
			try {
				usfDispatcher.processingAction(actionUri);
			} catch (Exception e) {
                log.error(e);
				throw new ServletException(e);
			} finally {
				ActionContext.setContext(null);
			}
		} else {
			chain.doFilter(request, response);
		}
	}

	/**
	 * {@link UsfDispatcher} 소멸.
	 */
	public void destroy() {
		try {
			if (jvmMBean != null) {
				JMXUtil.unregisterMBean(jvmMBean);
				jvmMBean = null;
			}
			if (webModuleMBean != null) {
				JMXUtil.unregisterMBean(webModuleMBean);
				webModuleMBean.clear();
				webModuleMBean = null;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		this.usfDispatcher.destroy();
	}

	/**
	 * requet의 encoding 변경
	 * 
	 * @param request 	private void setCharacterEncoding(HttpServletRequest
	 * @param encoding String
	 * @throws IOException the IOException
	 */
	private void setCharacterEncoding(HttpServletRequest request, String encoding) throws IOException {
		request.setCharacterEncoding(encoding);
		request.getParameter("dummy");
	}

	/**
	 * request가 multipart인지 확인
	 * 
	 * @return boolean
	 * @param request HttpServletRequest
	 */
	private boolean isMultiPart(HttpServletRequest request) {
		String contentType = request.getContentType();
		return contentType != null && contentType.indexOf("multipart/form-data") != -1;
	}
	
	/**
	 * @return {@link UsfDispatcher}
	 * */
	public UsfDispatcher getUsfDispatcher() {
		return usfDispatcher;
	}
}
